#include "serve.h"

Server::Server()
{
    // 创建监听套接字
    listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    if ( listen_socket == -1 ) {
        cerr << "Failed to create listen socket" << endl;
        exit(1);
    }

    // 设置监听套接字为非阻塞模式
    fcntl(listen_socket, F_SETFL, O_NONBLOCK);

    // 设置服务器地址结构
    sockaddr_in server_addr{};
    server_addr.sin_family      = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port        = htons(PORT);

    // 绑定地址和端口
    if ( bind(listen_socket, reinterpret_cast<sockaddr*>(&server_addr), sizeof(server_addr)) == -1 ) {
        cerr << "Bind failed" << endl;
        close(listen_socket);
        exit(1);
    }

    // 监听连接请求
    if ( listen(listen_socket, SOMAXCONN) == -1 ) {
        cerr << "Listen failed" << endl;
        close(listen_socket);
        exit(1);
    }

    // 创建 epoll 实例
    epoll_fd = epoll_create1(0);
    if ( epoll_fd == -1 ) {
        cerr << "Failed to create epoll instance" << endl;
        close(listen_socket);
        exit(1);
    }

    // 添加监听套接字到 epoll 实例中
    epoll_event event{};
    event.events  = EPOLLIN | EPOLLET;
    event.data.fd = listen_socket;
    if ( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_socket, &event) == -1 ) {
        cerr << "Failed to add listen socket to epoll" << endl;
        close(listen_socket);
        close(epoll_fd);
        exit(1);
    }

    // 创建事件数组
    events.resize(MAX_EVENTS);
}

void Server::run()
{
    cout << "Server started, listening on port " << PORT << "..." << endl;
    while ( true ) {
        // 等待事件发生
        int num_events = epoll_wait(epoll_fd, events.data(), MAX_EVENTS, -1);
        if ( num_events == -1 ) {
            cerr << "epoll_wait failed" << endl;
            exit(1);
        }

        // 处理就绪事件
        for ( int i = 0; i < num_events; ++i ) {
            int fd = events[i].data.fd;
            if ( fd == listen_socket ) {
                // 有新连接请求
                acceptConnection();
            }
            else {
                // 收到消息
                receiveMessage(fd);
            }
        }
    }
}

void Server::acceptConnection()
{
    sockaddr_in client_addr{};
    socklen_t   client_addr_len = sizeof(client_addr);
    int         client_socket   = accept(listen_socket, reinterpret_cast<sockaddr*>(&client_addr), &client_addr_len);
    if ( client_socket == -1 ) {
        cerr << "Accept failed" << endl;
        return;
    }

    // 设置客户端套接字为非阻塞模式
    fcntl(client_socket, F_SETFL, O_NONBLOCK);

    // 添加客户端套接字到 epoll 实例中
    epoll_event event{};
    event.events  = EPOLLIN | EPOLLET;
    event.data.fd = client_socket;
    if ( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket, &event) == -1 ) {
        cerr << "Failed to add client socket to epoll" << endl;
        close(client_socket);
        return;
    }

    // 发送终端界面给客户端
    string terminalInterface = "Welcome to the chat server!\n"
                               "Options:\n"
                               "1. Register new user\n"
                               "2. Login existing user\n"
                               "3. Exit\n";
    send(client_socket, terminalInterface.c_str(), terminalInterface.size(), 0);

    // 创建新用户并保存到用户信息哈希表中
    UserInfo userInfo;
    // 未登录状态
    userInfo.state       = LIANJIE;
    users[client_socket] = userInfo;

    cout << "New connection from " << inet_ntoa(client_addr.sin_addr) << ":" << ntohs(client_addr.sin_port) << endl;
}

void Server::receiveMessage(int sockfd)
{
    char    buffer[1024];
    ssize_t bytes_received = recv(sockfd, buffer, sizeof(buffer), 0);
    if ( bytes_received <= 0 ) {
        // 客户端断开连接
        cout << "Client disconnected: " << sockfd << endl;
        close(sockfd);
        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sockfd, nullptr);
        users.erase(sockfd);
    }
    else {
        // 处理客户端发送的消息
        string message(buffer, bytes_received);
        handleMessage(sockfd, message);
    }
}

void Server::handleMessage(int sockfd, const string& message)
{
    // 找到对应的user_info
    UserInfo* userInfo = &(users[sockfd]);
    if ( message == "Q!" ) {
        userInfo->state = EXIT;
    }

    string string1;
    switch ( userInfo->state ) {
    case LIANJIE:
        // 根据消息类型执行相应操作
        if ( message == "1" ) {
            // 注册新用户
            userInfo->state = ZHUCE;
            string1         = "请输入用户名\n";
            send(sockfd, string1.c_str(), string1.size(), 0);
        }
        else if ( message == "2" ) {
            // 登录已有用户
            userInfo->state = DENGLU;
            string1         = "请输入用户名\n";
            send(sockfd, string1.c_str(), string1.size(), 0);
        }
        else if ( message == "3" ) {
            // 退出
            userInfo->state = EXIT;
        }
        else {
            // 未知选项，发送错误消息给客户端
            string errorMessage = "Invalid option. Please select 1, 2, or 3.\n";
            send(sockfd, errorMessage.c_str(), errorMessage.size(), 0);
        }
        break;

    case ZHUCE:
        userInfo->username = message;
        userInfo->state    = ZHUCE2;

        string1 = "请输入密码\n";
        send(sockfd, string1.c_str(), string1.size(), 0);
        break;

    case ZHUCE2:
        userInfo->password = message;
        userInfo->state    = LIAOTIAN;

        {
            UserData userData;
            userData.password = message;
            userData.sockfd   = sockfd;
            userData.online   = true;

            user_reg[userInfo->username] = userData;
        }
        cout << "current users " << user_reg.size() << endl;

        string1 = "已经进行自动登录\n"
                  "输入 yes 开始聊天\n";
        send(sockfd, string1.c_str(), string1.size(), 0);
        break;

    case DENGLU:
        if ( user_reg.find(message) != user_reg.end() ) {
            auto& user = user_reg[message];
            if ( user.online ) {
                string1 = "已经在其他设备登陆\n"
                          "请重新输入登陆用户名\n";
                send(sockfd, string1.c_str(), string1.size(), 0);
                break;
            }
            else {
                userInfo->username = message;
                userInfo->state    = DENGLU2;

                string1 = "请输入密码\n";
                send(sockfd, string1.c_str(), string1.size(), 0);
            }
        }
        break;

    case DENGLU2:
    {
        string password1 = user_reg[userInfo->username].password;
        if ( password1 == message ) {
            string1 = "登陆成功\n"
                      "输入 yes 开始聊天\n";
            send(sockfd, string1.c_str(), string1.size(), 0);

            userInfo->password = message;
            userInfo->state    = LIAOTIAN;

            user_reg[userInfo->username].online = true;
            user_reg[userInfo->username].sockfd = sockfd;
        }
        // 密码错误
        else {
            string1 = "密码错误，请重新输入密码\n";
            send(sockfd, string1.c_str(), string1.size(), 0);
        }
    } break;

    case LIAOTIAN:
        string1 = "需要给谁发消息\n";
        send(sockfd, string1.c_str(), string1.size(), 0);

        userInfo->state = LIAOTIAN2;
        break;

    case LIAOTIAN2:
        // 查找是否有该用户
        if ( user_reg.find(message) != user_reg.end() ) {
            // 检查是否在线
            if ( user_reg[message].online ) {
                string1 = "请输入需要发送的消息\n";
                send(sockfd, string1.c_str(), string1.size(), 0);
                userInfo->targetname = message;
                userInfo->state      = LIAOTIAN3;
            }
            else {
                string1 = "对方不在线\n";
                send(sockfd, string1.c_str(), string1.size(), 0);
                userInfo->state = LIAOTIAN;
            }
        }
        // 查询不到该用户
        else {
            string1 = "查询不到该用户\n"
                      "输入 yes 重新开始聊天\n";
            send(sockfd, string1.c_str(), string1.size(), 0);
            userInfo->state = LIAOTIAN;
        }
        break;

    case LIAOTIAN3:
    {
        int sockfd_tar = user_reg[userInfo->targetname].sockfd;

        //        send(sockfd_tar, message.c_str(), message.size(), 0);
        string1 = "\n" + userInfo->username + ":" + message + "\n";
        send(sockfd_tar, string1.c_str(), string1.size(), 0);

        string1 = "发送成功\n"
                  "需要给谁发消息\n";
        send(sockfd, string1.c_str(), string1.size(), 0);

        userInfo->state = LIAOTIAN2;
    } break;

    case EXIT:
        break;
    }

    if ( userInfo->state == EXIT ) {
        close(sockfd);
        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sockfd, nullptr);
        user_reg[userInfo->username].online = false;
        users.erase(sockfd);
        cout << "Client exited: " << sockfd << endl;
    }
}
